/***********************************************************************
 * This file is a basic template for creating C code for a terminal    *
 * device.  Copy this file into your project directory and modify or   *
 * add to it as needed.                                                *  
 *                                                                     *
 * If interrupts are not used, all code presented for that interrupt   *
 * can be removed or commented out with C-style comment declarations.  *
 *                                                                     *
 * For additional information about dsPIC architecture and language    *
 * tools, refer to the following documents:                            *
 *                                                                     *
 * 16-Bit Language Tools Getting Started                  : DS51316    *
 * MPLAB C Compiler for                                                *
 *           PIC24 MCUs and dsPIC DSCs User's Guide       : DS51284    *
 * MPLAB Assembler, Linker and Utilities for                           *
 *           PIC24 MCUs and dsPIC DSCs User's Guide       : DS51317    *
 * 16-Bit Language Tools Libraries                        : DS51456    *
 *                                                                     *
 * For the latest Datasheets and Family Reference Guides, please       *
 * see the Microchip web site.                                         *
 *                                                                     *
 * Template file has been compiled with MPLAB C30 v3.20.               *
 *                                                                     *
 ***********************************************************************
 *                                                                     * 
 *    Author:                                                          *
 *    Company:                                                         * 
 *    Filename:       Terminal.c                                       *
 *    Date:           3/8/2009                                         *
 *    File Version:   0.0.1                                            *
 *    Other Files Required: p24FJ16GA002.h, p24FJ16GA002.gld           *
 *    Tools Used: MPLAB IDE -> 8.10                                    *
 *                Compiler  -> 3.20                                    *
 *                                                                     *
 *    Devices Supported:                                               *
 *                p24FJ16GA002                                         *
 *                p24FJ16GA004                                         *
 ***********************************************************************
 *                                                                     *
 * Other Comments:                                                     *
 *                                                                     *
 * 1) C attributes, designated by the __attribute__ keyword, provide a *
 *    means to specify various characteristics of a variable or        *
 *    function, such as where a particular variable should be placed   *
 *    in memory, whether the variable should be aligned to a certain   *
 *    address boundary, whether a function is an Interrupt Service     *
 *    Routine (ISR), etc. If no special characteristics need to be     *
 *    specified for a variable or function, then attributes are not    *
 *    required. For more information about attributes, refer to the    *
 *    C30 User's Guide.                                                *
 *                                                                     *
 * 2) The space(xmemory) and space(ymemory) attributes are             *
 *    used to place a variable in X data space and Y data space,       *
 *    respectively. Variables accessed by dual-source DSP instructions *
 *    must be defined using these attributes.                          *
 *                                                                     *
 * 3) The aligned(k) attribute, used in variable definitions, is used  *
 *    to align a variable to the nearest higher 'k'-byte address       * 
 *    boundary. 'k' must be substituted with a suitable constant       *
 *    number when the ModBuf_X(k) or ModBuf_Y(k) macro is invoked.     *
 *    In most cases, variables are aligned either to avoid potential   *
 *    misaligned memory accesses, or to configure a modulo buffer.     *
 *                                                                     *
 * 4) The interrupt attribute is used to qualify a function as an      *
 *    interrupt service routine. An interrupt routine can be further   *
 *    configured to save certain variables on the stack, using the     *
 *    save(var-list) directive. If constants or string literals may    *
 *    be accessed using the default constants-in-code memory model,    *
 *    then the auto_psv attribute is included. If no constants or      *
 *    string literals will be accessed, the no_auto_psv attribute      *
 *    can be included, saving a few cycles of latency.                 *
 *                                                                     *
 * 5) The shadow attribute is used to set up any function to           *
 *    perform a fast context save using shadow registers.              *
 *                                                                     *
 * 6) Note the use of double-underscores (__) at the start and end of  *
 *    the attribute keyword mentioned above.                           *
 *                                                                     *
 **********************************************************************/

/* The correct device header file will be selected automatically */

#ifdef _DEBUG
#include <stdio.h>
extern BOOL Indicator_Creat(UINT8 sort);
extern UINT8 Request_process(T_ReqIndbuf commbuf);
#endif

#define USE_AND_OR
#include "p24FJ16GA002.h"
#include "PwrMgnt.h"
#include "Terminal.h"
#include "delay.h"
#include "timer.h"
#include "wdt.h"

#ifndef __DEBUG
//Macros for Configuration Fuse Registers:
//Invoke macros to set up  device configuration fuse registers.
//The fuses will select the oscillator source, power-up timers,
//watch-dog timers etc. The macros are defined within the device
//header files.
// The configuration fuse registers reside in Flash memory.
// JTAG/Code Protect/Write Protect/Clip-on Emulation mode/Watchdog Timer/ICD pins select
_CONFIG1(JTAGEN_OFF & GCP_OFF & GWRP_OFF & COE_OFF & FWDTEN_OFF & ICS_PGx2)
// Enable CLK switch, Disable CLK monitor, OSCO or Fosc/2, Primary Oscillator Mode: Disabled, 
//Internal Fast RC oscillator w/ divide and PLL
_CONFIG2(FCKSM_CSDCMD & OSCIOFNC_ON & POSCMOD_NONE & FNOSC_FRCPLL)
#endif
/* Define constants here                            */
#define CONSTANT1 		10
#define CONSTANT2 		20
#define PERIOD2S  0x400 - 1
#define PERIOD2M  0x400 * 60 - 1
#define PUBLIC_ADDRESS 0xCCCCCCCC
#define PUBLIC_CHANNEL 0x2
/************* START OF GLOBAL DEFINITIONS **********/


/* Define arrays: array1[], array2[], etc.          */
/* with attributes, as given below                  */

/* either using the entire attribute                */
int array1[CONSTANT1] __attribute__((space(data), aligned(32)));
int array2[CONSTANT1] __attribute__((space(data), aligned(32)));

/* or using macros defined in the header file       */
int array3[CONSTANT1] _BSS(32);
int array4[CONSTANT1] _BSS(32);

/* Define arrays without attributes                 */
int array5[CONSTANT2]; /* array5 is NOT an aligned buffer */

/* ------------------------------------------------ */

/* Define global variables with attributes          */
UINT InterruptCounter __attribute__((space(data)));
UINT MotionDetected __attribute__((space(data)));
int variable1 __attribute__((space(data)));
int variable2 __attribute__((space(data)));
T_terminal Terminal __attribute__((space(data)));
int old_ipl __attribute__((space(data)));
BOOL focus_on __attribute__((space(data)));
/* Define global variables without attributes       */
int variable3;

/************** END OF GLOBAL DEFINITIONS ***********/
// External function declare
extern void Config2401(void);
extern void nRF2401_port_init();
extern void RF_config(unsigned long int rec_addr, unsigned char power, unsigned char channal);
extern void nRF2401_SetTxMode(void);
extern void nRF2401_SetRxMode(void);
extern void nRF2401_set_tx_addr(unsigned long int addr);
extern void nRF2401_TxPacket(T_ReqIndbuf* TxBuff);
extern unsigned char nRF2401_RxPacket(T_ReqIndbuf* RxBuff);
extern UINT8 Indicator_process(T_ReqIndbuf commbuf);
extern UINT8 Request_Creat(UINT8 state);
extern void MMA7455_Init(void);
extern void MMA_Read_Stat(unsigned char *xp);
#ifdef _DEBUG
extern BOOL Indicator_Creat(UINT8 sort);
extern UINT8 Request_process(T_ReqIndbuf commbuf);
#endif
// Local function definitions
int terminalReset(RESET_SOURCE ResetSource);
void terminalSmIdle(void);
void terminalSmNormal(void);
void terminalSmAbnormal(void);

/*
 *work flow of every wake_up
 */
BOOL wakeup_workflow(terminalState_t state)
{
    UINT loopcounter = 0;

    // Wait for PLL to lock
    while (OSCCONbits.LOCK != 1);
   
    //power up and config nRF2401
    if (Terminal.runtimePara.lastState != state) //state shift occur
    {
        switch (state)
        {
            default:
            case terminalStateIdle:
            {
                RF_config(Terminal.SerialNo
                        , TERMINAL_POWER_10DB
                        , PUBLIC_CHANNEL
                        );
                nRF2401_set_tx_addr(PUBLIC_ADDRESS);
            }
            case terminalStateAbnormal:            
            {
                RF_config(Terminal.runtimePara.Terminal_ID
                        , TERMINAL_POWER_10DB
                        , PUBLIC_CHANNEL
                        );
                nRF2401_set_tx_addr(PUBLIC_ADDRESS);
            }
                break;
            case terminalStateNormal:
            {
                RF_config(Terminal.runtimePara.Terminal_ID
                        , Terminal.runtimePara.Terminal_Power
                        , Terminal.runtimePara.Terminal_Channel
                        );
                nRF2401_set_tx_addr(Terminal.runtimePara.Node_ID);
            }
                break;
          }
        Config2401(); //TODO: add power up nRF2401 code
    }
    //motion detection
    if (MotionDetected != 0)
    {
        Terminal.runtimePara.Terminal_Status += TERMINAL_STATUS_MOVE;
    }
    //nRF2401 TX
    //TODO: add nRF2401 set TX mode
    nRF2401_SetTxMode();

    if (Request_Creat(state) == TRUE)
    {
        //TODO: add nRF2401 TX data code
        nRF2401_TxPacket(&Terminal.Reqbuf);
    }
    //nRF2401 RX
    //TODO: add nRF2401 RX code
    nRF2401_SetRxMode();
    while (nRF2401_RxPacket(&Terminal.Indbuf) == 0)
    {
        delay_ms(3);
        loopcounter++;
        if (loopcounter == 3)//no respose from Node
        {
            Terminal.runtimePara.lastState = Terminal.runtimePara.currentState;
            Terminal.runtimePara.currentState = terminalStateIdle;
            return ( FALSE);
        }
    }

    return ( TRUE);
}
/*
State Machine table
 */
void (* const terminalJumpTable[terminalStateNumber])(void) = {
    terminalSmIdle,
    terminalSmNormal,
    terminalSmAbnormal
};

/*************************************************************************
 *
 * IDLE state function
 *
 *************************************************************************/
void terminalSmIdle(void)
{
    //TODO: add Idle state code here
    if (wakeup_workflow(terminalStateIdle) == TRUE)
    {
        if (Indicator_process(Terminal.Indbuf) == FALSE)
        {
            Terminal.runtimePara.lastState = Terminal.runtimePara.currentState;
            Terminal.runtimePara.currentState = terminalStateIdle;
        }
    }
}

/*************************************************************************
 *
 * Normal state function
 *
 *************************************************************************/
void terminalSmNormal(void)
{
    //TODO: add Idle state code here
    if (wakeup_workflow(terminalStateNormal) == TRUE)
    {
        if (Indicator_process(Terminal.Indbuf) == FALSE)
        {
            Terminal.runtimePara.lastState = Terminal.runtimePara.currentState;
            Terminal.runtimePara.currentState = terminalStateIdle;
        }
    }
}

/*************************************************************************
 *
 * Abnormal state function
 *
 *************************************************************************/
void terminalSmAbnormal(void)
{
    //TODO: add Idle state code here
    if (wakeup_workflow(terminalStateAbnormal) == TRUE)
    {
        if (Indicator_process(Terminal.Indbuf) == FALSE)
        {
            Terminal.runtimePara.lastState = Terminal.runtimePara.currentState;
            Terminal.runtimePara.currentState = terminalStateIdle;
        }
    }
}

/*

 */
void SWReset(void)
{
    Terminal.SerialNo = PRODUCT_SERIAL_NO;
    Terminal.runtimePara.Terminal_ID = 0;
    Terminal.runtimePara.Terminal_Status = TERMINAL_STATUS_OK;
    Terminal.runtimePara.Terminal_Channel = 0;
    Terminal.runtimePara.Terminal_Power = TERMINAL_POWER_10DB;
    Terminal.runtimePara.Terminal_Slot = 0;
    Terminal.runtimePara.Terminal_Time = 0;
    Terminal.runtimePara.currentState = terminalStateNumber; //triger config2401
    Terminal.runtimePara.lastState = terminalStateIdle;
    //Timer1 Reset value
    Terminal.runtimePara.HWConfig.Timer1Config.intConfig =
            T1_INT_ON
            | T1_INT_PRIOR_4;
    Terminal.runtimePara.HWConfig.Timer1Config.perConfig =
            T1_ON
            | T1_IDLE_CON
            | T1_GATE_OFF
            | T1_PS_1_64
            | T1_SYNC_EXT_OFF
            | T1_SOURCE_EXT;
    //Timer2 Reset value
    Terminal.runtimePara.HWConfig.Timer2Config.intConfig =
            T1_INT_ON
            | T1_INT_PRIOR_5;
    Terminal.runtimePara.HWConfig.Timer2Config.perConfig =
            T1_ON
            | T1_IDLE_CON
            | T1_GATE_OFF
            | T1_PS_1_8
            | T1_SYNC_EXT_OFF
            | T1_SOURCE_INT;
    //TODO: add other software configuration here
}

/*

 */
BOOL HWReset(T_HWConfig HWConfig)
{
    //TODO: add board initialization code here
    //MCU reset
    //IO port reset
    TRISBbits.TRISB4 = 1; //EXT CLOCK
    TRISAbits.TRISA4 = 1; //EXT CLOCK
    //Timer1/2 reset
    ConfigIntTimer1(Terminal.runtimePara.HWConfig.Timer1Config.intConfig);
    OpenTimer1(Terminal.runtimePara.HWConfig.Timer1Config.perConfig, PERIOD2S);
    ConfigIntTimer2(Terminal.runtimePara.HWConfig.Timer2Config.intConfig);
    CloseTimer2();
    //nRF2401 reset
    nRF2401_port_init();
    //7455 reset
    MMA7455_Init();
    return ( TRUE);
}

/*

 */
int terminalReset(RESET_SOURCE ResetSource)
{
    switch (ResetSource)
    {
        case POWER_ON_Reset://Reset when power on
        {
            //software reset
            SWReset();
            //hardware reset
            //Wait untill HW reset finish and OK
            while (HWReset(Terminal.runtimePara.HWConfig) == FALSE);
            //make the terminal run into idle state
            Terminal.runtimePara.currentState = terminalStateIdle;
        }
            break;
        case WATCHDOG_Reset://Reset when watchdog timer overflow
        case BURN_OUT_Reset://Reset when burn out
        case SOFTWARE_Reset://Reset due to software
        case EXTERNAL_Reset://Reset externally
        case CFG_WORD_MISMATCH_Reset://Reset when configure word mis matches
        case ILLEGAL_INSTR_Reset://Reset when illegal instruction is given
        case TRAP_Reset://Reset due to trap
        default://run after wakeup from sleep mode
        {
            //TODO: add code here
            //we go on the main loop run
            //make the terminal run into idle state
            Terminal.runtimePara.currentState = terminalStateIdle;
            return ( FALSE);
        }
            break;
    }
    return ( TRUE);
}

/************* START OF MAIN FUNCTION ***************/

/*

 */
int main(void)
{
#ifdef __DEBUG
    UINT index = 0;
    printf("%s\n", "Start main function");
#endif
    //disable watchdog
    EnableWDT(WDT_DISABLE);
    //software and hardware reset
    terminalReset(PwrMgnt_ResetSource());
    SET_CPU_IPL(4);
    //endless loop
    while (1)
    {
#ifdef __DEBUG
        printf("run into state: %d\n", Terminal.runtimePara.currentState);
        Indicator_Creat((index++) % 4);
#endif
        terminalJumpTable[Terminal.runtimePara.currentState]();
#ifndef __DEBUG
        Sleep();
#else
        Request_process(Terminal.Reqbuf);
#endif  


    }
    return (TRUE);
}

/****** START OF INTERRUPT SERVICE ROUTINES *********/

/* Replace the interrupt function names with the    */
/* appropriate names depending on interrupt source. */

/* The names of various interrupt functions for     */
/* each device are defined in the linker script.    */


/* Interrupt Service Routine 1                      */
/* for Timer1 overflow                              */

/* Fast context save (using push.s and pop.s)       */
void __attribute__((interrupt, shadow, auto_psv)) _T1Interrupt(void)
{
    /* TODO: add Interrupt Service Routine code goes here         */
    InterruptCounter = (InterruptCounter + 1) % (focus_on ? 1 : 60);
    T1_Clear_Intr_Status_Bit; /* clear interrupt flag */
    return;
}
/* Interrupt Service Routine                        */
/* for Timer2 overflow                              */

/* Fast context save (using push.s and pop.s)       */
void __attribute__((interrupt, shadow, auto_psv)) _T2Interrupt(void)
{
    /* TODO: add Interrupt Service Routine code goes here         */
    CloseTimer2();
    RESTORE_CPU_IPL(old_ipl);
    return;
}

/* Interrupt Service Routine 2: INT0Interrupt       */
/* for Motion Detection Interrupt                   */

/* Save and restore variables var1, var2, etc.      */
void __attribute__((interrupt(save(variable1, variable2)), auto_psv)) _INT0Interrupt(void)
{

    /* TODO: add Interrupt Service Routine code goes here         */

}

/* Interrupt Service Routine 3: INT1Interrupt       */
/* for Low battary power                            */

/* Save and restore variables var1, var2, etc.      */
void __attribute__((interrupt(save(variable1, variable2)), auto_psv)) _INT1Interrupt(void)
{

    /* TODO: add Interrupt Service Routine code goes here         */

}

/* Interrupt Service Routine 4: INT0Interrupt       */
/* for breaking detection                           */

/* Save and restore variables var1, var2, etc.      */
void __attribute__((interrupt(save(variable1, variable2)), auto_psv)) _INT2Interrupt(void)
{

    /* TODO: add Interrupt Service Routine code goes here         */

}
/********* END OF INTERRUPT SERVICE ROUTINES ********/

